package com.srtp.secureprotocol.main;

import android.content.Context;
import android.preference.PreferenceManager;

import androidx.annotation.NonNull;

import com.srtp.secureprotocol.cipherstream.*;
import com.srtp.secureprotocol.database.DatabaseFactory;
import com.srtp.secureprotocol.database.IdentityKeyDatabase;
import com.srtp.secureprotocol.database.IdentityKeyRecord;
import com.srtp.secureprotocol.keychain.Keychain;
import com.srtp.secureprotocol.recipient.LiveRecipientCache;
import com.srtp.secureprotocol.store.MyProtocolStore;
import com.srtp.secureprotocol.store.MySenderKeyStore;
import com.srtp.secureprotocol.util.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.signal.argon2.Argon2;
import org.signal.argon2.Argon2Exception;
import org.signal.argon2.Type;
import org.signal.argon2.Version;
import org.whispersystems.libsignal.DuplicateMessageException;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.InvalidKeyIdException;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.InvalidVersionException;
import org.whispersystems.libsignal.LegacyMessageException;
import org.whispersystems.libsignal.NoSessionException;
import org.whispersystems.libsignal.SessionBuilder;
import org.whispersystems.libsignal.SessionCipher;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.UntrustedIdentityException;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECKeyPair;
import org.whispersystems.libsignal.ecc.ECPrivateKey;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.groups.GroupCipher;
import org.whispersystems.libsignal.groups.GroupSessionBuilder;
import org.whispersystems.libsignal.groups.SenderKeyName;
import org.whispersystems.libsignal.groups.state.SenderKeyRecord;
import org.whispersystems.libsignal.groups.state.SenderKeyState;
import org.whispersystems.libsignal.groups.state.SenderKeyStore;
import org.whispersystems.libsignal.protocol.CiphertextMessage;
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
import org.whispersystems.libsignal.protocol.SenderKeyDistributionMessage;
import org.whispersystems.libsignal.protocol.SignalMessage;
import org.whispersystems.libsignal.state.PreKeyBundle;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
import org.whispersystems.libsignal.util.KeyHelper;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import static java.lang.Long.parseLong;

/**
 * 安全通信协议核心类
 * 提供端到端加密通信功能，包括：
 * 1. 密钥初始化与管理
 * 2. 点对点加密会话
 * 3. 粘性会话（持久化群组会话）
 * 4. 文件加密传输
 * 5. 基于Argon2的密码派生
 */
public class SecureProtocol {

    private static Context context;          // 应用上下文
    private static String path;              // 文件存储路径
    private static LiveRecipientCache recipientCache;  // 接收方缓存
    private final Keychain keychain;         // 密钥链管理
    private final String passwordKey;        // 密码存储键

    /**
     * 构造函数
     * @param context 应用上下文
     * @param service 服务标识符
     */
    public SecureProtocol(Context context, String service) {
        SecureProtocol.context = context;
        path = context.getFilesDir().getPath();
        keychain = new Keychain(context);
        this.passwordKey = service + ".password";  // 构建密码存储键
    }

    public static void setPathForTesting(String testPath) {
        path = testPath;
    }

    // ------------------------ 初始化方法 ------------------------

    /**
     * 协议初始化方法（注册时调用）
     * @param userId 用户唯一ID
     * @param password 用户明文密码
     * @param progressEvent 进度回调接口
     * @return 包含密钥数据的JSON对象：
     *   - 身份密钥
     *   - 签名预密钥
     *   - 预密钥组
     *   - 本地ID
     *   - 一次性ID
     *   - 密码哈希值
     *   - 密码盐值
     */
    public JSONObject initialize(String userId, String password, ProgressEvent progressEvent) {
        try {
            // 密码存储到安全存储区
            HashMap<String, String> serviceMap = new HashMap();
            serviceMap.put("service", passwordKey);
            keychain.setGenericPassword(userId, userId, password, serviceMap);

            // 生成密码盐值
            SecureRandom randomSalt = new SecureRandom();
            byte[] salt = new byte[32];
            randomSalt.nextBytes(salt);

            // 使用Argon2派生密码哈希
            byte[] passwordHashBytes = new Argon2.Builder(Version.V13)
                    .type(Type.Argon2id)
                    .memoryCostKiB(4 * 1024)
                    .parallelism(2)
                    .iterations(3)
                    .hashLength(32)
                    .build()
                    .hash(password.getBytes(), salt)
                    .getHash();
            String passwordHash = Base64.encodeBytes(passwordHashBytes);

            SignalProtocolStore store = new MyProtocolStore(context);
            IdentityKeyUtil.generateIdentityKeys(context);
            IdentityKeyPair identityKey = store.getIdentityKeyPair();
            SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(context, identityKey, true);
            List<PreKeyRecord> preKeys = PreKeyUtil.generatePreKeys(context, 0, 10);
            JSONArray preKeysArray = new JSONArray();
            for (int i = 0; i < preKeys.size(); i++) {
                JSONObject preKey = new JSONObject();
                preKey.put("id", preKeys.get(i).getId());
                preKey.put("publicKey", Base64.encodeBytes(preKeys.get(i).getKeyPair().getPublicKey().serialize()));
                HashMap<String, String> cipherMap = pbEncrypt(preKeys.get(i).getKeyPair().getPrivateKey().serialize(), password);
                preKey.put("cipher", cipherMap.get("cipher"));
                preKey.put("salt", cipherMap.get("salt"));
                preKeysArray.put(preKey);
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", i + 1);
                    event.put("total", preKeys.size());
                    progressEvent.execute(event);
                }
            }

            // 生成身份密钥对
            JSONObject signedPreKeyJson = new JSONObject();
            signedPreKeyJson.put("id", Preferences.getActiveSignedPreKeyId(context));
            signedPreKeyJson.put("publicKey", Base64.encodeBytes(signedPreKey.getKeyPair().getPublicKey().serialize()));
            signedPreKeyJson.put("signature", Base64.encodeBytes(signedPreKey.getSignature()));
            HashMap<String, String> signedCipherMap = pbEncrypt(signedPreKey.getKeyPair().getPrivateKey().serialize(), password);
            signedPreKeyJson.put("cipher", signedCipherMap.get("cipher"));
            signedPreKeyJson.put("salt", signedCipherMap.get("salt"));
            signedPreKeyJson.put("timestamp", Long.toString(signedPreKey.getTimestamp()));

            // 生成签名预密钥
            JSONObject identityKeyJson = new JSONObject();
            identityKeyJson.put("id", Preferences.getActiveIdentityKeyId(context));
            identityKeyJson.put("publicKey", Base64.encodeBytes(identityKey.getPublicKey().serialize()));
            HashMap<String, String> identityCipherMap = pbEncrypt(identityKey.getPrivateKey().serialize(), password);
            identityKeyJson.put("cipher", identityCipherMap.get("cipher"));
            identityKeyJson.put("salt", identityCipherMap.get("salt"));
            identityKeyJson.put("timestamp", Preferences.getActiveIdentityKeyTimestamp(context));

            // 生成预密钥组
            String oneTimeId = UUID.randomUUID().toString();
            int localId = KeyHelper.generateRegistrationId(false);
            Preferences.setLocalRegistrationId(context, localId);
            JSONObject map = new JSONObject();
            map.put("identityKey", identityKeyJson);
            map.put("signedPreKey", signedPreKeyJson);
            map.put("preKeys", preKeysArray);
            map.put("passwordHash", passwordHash);
            map.put("passwordSalt", Base64.encodeBytes(salt));
            map.put("oneTimeId", oneTimeId);
            map.put("localId", localId);
            PreferenceManager.getDefaultSharedPreferences(context).edit().putString("oneTimeId", oneTimeId).apply();
            PreferenceManager.getDefaultSharedPreferences(context).edit().putString("userId", userId).apply();

            // 构建返回数据
            return map;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 协议重新初始化（登录时调用）
     * @param bundle 密钥数据包
     * @param password 用户明文密码
     * @param userId 用户唯一ID
     * @param progressEvent 进度回调接口
     */
    public void reInitialize(JSONObject bundle, String password, String userId, ProgressEvent progressEvent) {
        try {
            PreferenceManager.getDefaultSharedPreferences(context).edit().putString("userId", userId).apply();

            // 从bundle解析密钥数据
            HashMap<String, String> serviceMap = new HashMap();
            serviceMap.put("service", context.getPackageName());
            keychain.setGenericPassword(userId, userId, password, serviceMap);


            SignalProtocolStore store = new MyProtocolStore(context);
            Preferences.setLocalRegistrationId(context, bundle.getInt("localId"));
            JSONArray preKeys = (JSONArray) bundle.get("preKeys");
            JSONArray senderKeys = (JSONArray) bundle.get("senderKeys");
            JSONArray signedPreKeys = (JSONArray) bundle.get("signedPreKeys");  //签名数据，数据完整性验证
            JSONArray identityKeys = (JSONArray) bundle.get("identityKeys");    //身份验证（登录、发送消息、解密信息）
            int totalKeys = identityKeys.length() + signedPreKeys.length() + preKeys.length() + senderKeys.length();
            int progress = 0;

            for (int i = 0; i < identityKeys.length(); i++) {
                JSONObject IKJson = identityKeys.getJSONObject(i);
                IdentityKey publicKey = new IdentityKey(Base64.decode((String) IKJson.getString("publicKey")), 0);
                byte[] identityCipher = pbDecrypt((String) IKJson.getString("cipher"), (String) IKJson.getString("salt"), password);
                ECPrivateKey privateKey = Curve.decodePrivatePoint(identityCipher);
                ECKeyPair ecKeyPair = new ECKeyPair(publicKey.getPublicKey(), privateKey);
                int identityKeyId = IKJson.getInt("id");
                IdentityKeyRecord identityKeyRecord = new IdentityKeyRecord(identityKeyId, parseLong(IKJson.getString("timestamp")), ecKeyPair);
                DatabaseFactory.getIdentityKeyDatabase(context).insertIdentityKey(identityKeyId, identityKeyRecord);
                if (IKJson.getBoolean("active")) {
                    IdentityKeyUtil.setActive(context, "pref_identity_public", Base64.encodeBytes(publicKey.serialize()));
                    IdentityKeyUtil.setActive(context, "pref_identity_private", Base64.encodeBytes(privateKey.serialize()));
                    Preferences.setNextIdentityKeyId(context, identityKeyId + 1);
                    Preferences.setActiveIdentityKeyId(context, identityKeyId);
                    Preferences.setActiveIdentityKeyTimestamp(context, parseLong(IKJson.getString("timestamp")));
                }

                // PROGRESS
                progress += 1;
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", progress);
                    event.put("total", totalKeys);
                    progressEvent.execute(event);
                }
            }

            for (int i = 0; i < signedPreKeys.length(); i++) {
                JSONObject SPKJson = signedPreKeys.getJSONObject(i);
                ECPublicKey sigPublicKey = Curve.decodePoint(Base64.decode((String) SPKJson.getString("publicKey")), 0);
                byte[] signedCipher = pbDecrypt((String) SPKJson.getString("cipher"), (String) SPKJson.getString("salt"), password);
                ECPrivateKey sigPrivateKey = Curve.decodePrivatePoint(signedCipher);
                ECKeyPair sigKeyPair = new ECKeyPair(sigPublicKey, sigPrivateKey);
                int signedPreKeId = (int) SPKJson.get("id");
                SignedPreKeyRecord record = new SignedPreKeyRecord(signedPreKeId, parseLong(SPKJson.getString("timestamp")), sigKeyPair, Base64.decode(SPKJson.getString("signature")));
                store.storeSignedPreKey(signedPreKeId, record);
                if (SPKJson.getBoolean("active")) {
                    Preferences.setActiveSignedPreKeyId(context, signedPreKeId);
                    Preferences.setActiveSignedPreKeyTimestamp(context, parseLong(SPKJson.getString("timestamp")));
                    Preferences.setNextSignedPreKeyId(context, signedPreKeId + 1);
                }

                // PROGRESS
                progress += 1;
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", progress);
                    event.put("total", totalKeys);
                    progressEvent.execute(event);
                }
            }

            for (int i = 0; i < preKeys.length(); i++) {
                JSONObject preKeyJson = preKeys.getJSONObject(i);
                ECPublicKey prePubKey = Curve.decodePoint(Base64.decode(preKeyJson.getString("publicKey")), 0);
                byte[] cipher = pbDecrypt(preKeyJson.getString("cipher"), preKeyJson.getString("salt"), password);
                ECPrivateKey prePrivKey = Curve.decodePrivatePoint(cipher);
                ECKeyPair preKey = new ECKeyPair(prePubKey, prePrivKey);
                PreKeyRecord pkRecord = new PreKeyRecord(preKeyJson.getInt("id"), preKey);
                store.storePreKey(preKeyJson.getInt("id"), pkRecord);

                // PROGRESS
                progress += 1;
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", progress);
                    event.put("total", totalKeys);
                    progressEvent.execute(event);
                }
            }

            // OWN SENDER KEYS
            for (int i = 0; i < senderKeys.length(); i++) {
                JSONObject senderKeyJson = senderKeys.getJSONObject(i);
                reinitMyStickySession(userId, senderKeyJson);

                // PROGRESS
                progress += 1;
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", progress);
                    event.put("total", totalKeys);
                    progressEvent.execute(event);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 批量解密预密钥
     * @param preKeys 预密钥JSON数组
     */
    public void decryptPreKeys(JSONArray preKeys) throws Exception {
        String userId = PreferenceManager.getDefaultSharedPreferences(context).getString("userId", "");
        HashMap<String, String> serviceMap = new HashMap();
        serviceMap.put("service", passwordKey);
        String password = keychain.getGenericPassword(userId, serviceMap);
        SignalProtocolStore store = new MyProtocolStore(context);
        for (int i = 0; i < preKeys.length(); i++) {
            JSONObject preKeyJson = preKeys.getJSONObject(i);
            ECPublicKey prePubKey = Curve.decodePoint(Base64.decode(preKeyJson.getString("publicKey")), 0);
            byte[] cipher = pbDecrypt(preKeyJson.getString("cipher"), preKeyJson.getString("salt"), password);
            ECPrivateKey prePrivKey = Curve.decodePrivatePoint(cipher);
            ECKeyPair preKey = new ECKeyPair(prePubKey, prePrivKey);
            PreKeyRecord pkRecord = new PreKeyRecord(preKeyJson.getInt("id"), preKey);
            store.storePreKey(preKeyJson.getInt("id"), pkRecord);
        }
    }

    /**
     * 创建密码哈希值
     * @param password 明文密码
     * @param salt 盐值
     * @return 密码哈希值
     */
    public String createPasswordHash(String password, String salt) throws IOException, Argon2Exception {
        byte[] passwordHashBytes = new Argon2.Builder(Version.V13)
                .type(Type.Argon2id)
                .memoryCostKiB(4 * 1024)
                .parallelism(2)
                .iterations(3)
                .hashLength(32)
                .build()
                .hash(password.getBytes(), Base64.decode(salt))
                .getHash();
        // 使用Argon2派生密码哈希
        return Base64.encodeBytes(passwordHashBytes);
    }


    /**
     * 创建新密码哈希（用于密码更改）
     * @param password 明文密码
     * @return 包含新哈希和盐值的JSON对象
     */
    public JSONObject createNewPasswordHash(String password) {
        try {
            // 生成新盐值
            SecureRandom randomSalt = new SecureRandom();
            byte[] salt = new byte[32];
            randomSalt.nextBytes(salt);


            // 派生新密码哈希
            byte[] passwordHashBytes = new Argon2.Builder(Version.V13)
                    .type(Type.Argon2id)
                    .memoryCostKiB(4 * 1024)
                    .parallelism(2)
                    .iterations(3)
                    .hashLength(32)
                    .build()
                    .hash(password.getBytes(), salt)
                    .getHash();
            JSONObject map = new JSONObject();
            map.put("salt", Base64.encodeBytes(salt));
            map.put("hash", Base64.encodeBytes(passwordHashBytes));
            // 返回结果
            return map;
        } catch (Argon2Exception | JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 进度回调接口
     */
    public interface ProgressEvent {
        void execute(JSONObject event);
    }

    // ------------------------ 点对点会话 ------------------------

    /**
     * 初始化点对点会话
     * @param bundle 包含会话参数的JSON对象：
     *   - userId 用户ID
     *   - localId 本地ID
     *   - identityKey 身份公钥
     *   - signedPreKey 签名预公钥
     *   - signedPreKeyId 签名预密钥ID
     *   - signature 签名
     *   - preKey 预公钥
     *   - preKeyId 预密钥ID
     */
    public void initPairwiseSession(JSONObject bundle) {
        try {
            SignalProtocolStore store = new MyProtocolStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(bundle.getString("userId"), 0);
            SessionBuilder sessionBuilder = new SessionBuilder(store, signalProtocolAddress);
            ECPublicKey signedPreKey = Curve.decodePoint(Base64.decode(bundle.getString("signedPreKey")), 0);
            ECPublicKey identityKey = Curve.decodePoint(Base64.decode(bundle.getString("identityKey")), 0);
            IdentityKey identityPublicKey = new IdentityKey(identityKey);

            ECPublicKey preKey = null;
            int preKeyId = -1;
            if (bundle.has("preKey")) {
                preKey = Curve.decodePoint(Base64.decode(bundle.getString("preKey")), 0);
                preKeyId = bundle.getInt("preKeyId");
            }

            PreKeyBundle preKeyBundle = new PreKeyBundle(
                    bundle.getInt("localId"),
                    0,
                    preKeyId,
                    preKey,
                    bundle.getInt("signedPreKeyId"),
                    signedPreKey,
                    Base64.decode(bundle.getString("signature")),
                    identityPublicKey
            );
            sessionBuilder.process(preKeyBundle);
        } catch (UntrustedIdentityException | InvalidKeyException | IOException | JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * 点对点文本加密
     * @param userId 目标用户ID
     * @param text 明文文本
     * @return 加密后的Base64字符串
     */
    public String encryptTextPairwise(String userId, String text) {
        try {
            SignalProtocolStore store = new MyProtocolStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userId, 0);
            SessionCipher sessionCipher = new SessionCipher(store, signalProtocolAddress);
            CiphertextMessage cipher = sessionCipher.encrypt(text.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBytes(cipher.serialize());
        } catch (UntrustedIdentityException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 点对点文本解密
     * @param senderId 发送方ID
     * @param isStickyKey 是否是粘性密钥
     * @param cipher 加密文本
     * @return 解密后的明文
     */
    public String decryptTextPairwise(String senderId, boolean isStickyKey, String cipher) {
        try {
            SignalProtocolStore store = new MyProtocolStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
            SessionCipher sessionCipher = new SessionCipher(store, signalProtocolAddress);
            byte[] bytes;
            if (!store.containsSession(signalProtocolAddress) || isStickyKey) {
                PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(Base64.decode(cipher));
                bytes = sessionCipher.decrypt(preKeySignalMessage);
            } else {
                SignalMessage signalMessage = new SignalMessage(Base64.decode(cipher));
                bytes = sessionCipher.decrypt(signalMessage);
            }
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (InvalidMessageException | DuplicateMessageException | LegacyMessageException
                 | UntrustedIdentityException | InvalidVersionException | InvalidKeyIdException
                 | InvalidKeyException | NoSessionException | IOException e) {
            e.printStackTrace();
            SignalProtocolStore store = new MyProtocolStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
            SessionCipher sessionCipher = new SessionCipher(store, signalProtocolAddress);
            byte[] bytes = null;
            try {
                PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(Base64.decode(cipher));
                bytes = sessionCipher.decrypt(preKeySignalMessage);
            } catch (DuplicateMessageException | LegacyMessageException | InvalidMessageException
                     | InvalidKeyIdException | InvalidKeyException | UntrustedIdentityException
                     | InvalidVersionException | IOException ex) {
                ex.printStackTrace();
            }
            if (bytes != null)
                return new String(bytes, StandardCharsets.UTF_8);
            else
                return null;
        }
    }

    /**
     * 创建会话
     * @param userId 用户ID
     * @param sessionId 会话ID
     * @return 包含发送方密钥的JSON对象
     */
    public JSONObject createStickySession(String userId, String sessionId) {
        try {
            SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userId, 0);
            SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
            GroupSessionBuilder groupSessionBuilder = new GroupSessionBuilder(senderKeyStore);
            SenderKeyDistributionMessage senderKeyDistributionMessage = groupSessionBuilder.create(senderKeyName);
            DatabaseFactory.getStickyKeyDatabase(context).insertStickyKey(sessionId, Base64.encodeBytes(senderKeyDistributionMessage.serialize()));
            SenderKeyState senderKeyState = senderKeyStore.loadSenderKey(senderKeyName).getSenderKeyState();
            String key = Base64.encodeBytes(senderKeyState.getSenderChainKey().getSeed()) + Base64.encodeBytes(senderKeyState.getSigningKeyPrivate().serialize()) + Base64.encodeBytes(senderKeyState.getSigningKeyPublic().serialize());
            String encryptKey = encryptTextPairwise(userId, key);
            JSONObject map = new JSONObject();
            //加密的消息密钥
            map.put("id", senderKeyState.getKeyId());
            map.put("key", encryptKey);
            return map;
        } catch (InvalidKeyException | InvalidKeyIdException | JSONException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取发送方密钥
     * @param senderId 发送方ID
     * @param targetId 目标方ID
     * @param sessionId 会话ID
     * @param isSticky 是否是粘性会话
     * @return 加密后的发送方密钥
     */
    public String getSenderKey(String senderId, String targetId, String sessionId, Boolean isSticky) throws IOException, InvalidMessageException, LegacyMessageException {
        SenderKeyDistributionMessage senderKeyDistributionMessage = null;
        if (isSticky)
            senderKeyDistributionMessage = new SenderKeyDistributionMessage(Base64.decode(DatabaseFactory.getStickyKeyDatabase(context).getStickyKey(sessionId)));
        else {
            SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
            SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
            GroupSessionBuilder groupSessionBuilder = new GroupSessionBuilder(senderKeyStore);
            senderKeyDistributionMessage = groupSessionBuilder.create(senderKeyName);
        }
        return encryptTextPairwise(targetId, Base64.encodeBytes(senderKeyDistributionMessage.serialize()));
    }

    /**
     * 初始化粘性会话
     * @param senderId 发送方ID
     * @param sessionId 会话ID
     * @param cipherSenderKey 加密的发送方密钥
     * @param identityKeyId 身份密钥ID
     */
    public void initStickySession(String senderId, String sessionId, String cipherSenderKey, int identityKeyId) {
        try {
            if (cipherSenderKey != null) {
                SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
                SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
                SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
                GroupSessionBuilder groupSessionBuilder = new GroupSessionBuilder(senderKeyStore);
                String senderKey = decryptStickyKey(senderId, cipherSenderKey, identityKeyId);
                if (senderKey != null) {
                    SenderKeyDistributionMessage senderKeyDistributionMessage = new SenderKeyDistributionMessage(Base64.decode(senderKey));
                    groupSessionBuilder.process(senderKeyName, senderKeyDistributionMessage);
                }
            }
        } catch (InvalidMessageException | LegacyMessageException | IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 粘性会话文本加密
     * @param senderId 发送方ID
     * @param sessionId 会话ID
     * @param text 明文文本
     * @param isSticky 是否是粘性会话
     * @return 加密后的Base64字符串
     */
    public String encryptText(String senderId, String sessionId, String text, Boolean isSticky) {
        try {
            SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
            SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
            GroupCipher groupCipher = new GroupCipher(senderKeyStore, senderKeyName);
            byte[] cipherText;
            cipherText = groupCipher.encrypt(text.getBytes(StandardCharsets.UTF_8), isSticky);
            return Base64.encodeBytes(cipherText);
        } catch (NoSessionException | InvalidMessageException | DuplicateMessageException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 粘性会话文本解密
     * @param senderId 发送方ID
     * @param sessionId 会话ID
     * @param cipher 加密文本
     * @param isSticky 是否是粘性会话
     * @return 解密后的明文
     */
        public String decryptText(String senderId, String sessionId, String cipher, Boolean isSticky) {
        if (cipher.length() < 4)
            return null;
        try {
            Boolean isSelf = senderId.equals(PreferenceManager.getDefaultSharedPreferences(context).getString("userId", ""));
            SenderKeyStore mySenderKeyStore = new MySenderKeyStore(context);
            SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
            SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
            GroupCipher groupCipher = new GroupCipher(mySenderKeyStore, senderKeyName);
            byte[] decryptedCipher;
            decryptedCipher = groupCipher.decrypt(Base64.decode(cipher), isSticky, isSelf);
            return new String(decryptedCipher, StandardCharsets.UTF_8);
        } catch (LegacyMessageException | InvalidMessageException | DuplicateMessageException
                 | NoSessionException | IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 文件加密
     * @param senderId 发送方ID
     * @param sessionId 会话ID
     * @param filePath 文件路径
     * @param isSticky 是否是粘性会话
     * @return 包含加密文件URI和密钥的JSON对象
     */
    public JSONObject encryptFile(String senderId, String sessionId, String filePath, Boolean isSticky) throws JSONException {
        HashMap<String, String> hashMap = encryptBlob(filePath);
        String cipherText = encryptText(senderId, sessionId, hashMap.get("secret"), isSticky);
        JSONObject map = new JSONObject();
        map.put("uri", hashMap.get("uri"));
        map.put("cipher", cipherText);
        return map;
    }


    /**
     * 文件解密
     * @param senderId 发送方ID
     * @param sessionId 会话ID
     * @param filePath 加密文件路径
     * @param cipher 加密的密钥
     * @param outputPath 输出路径
     * @param isSticky 是否是粘性会话
     * @return 解密后的文件路径
     */
    public String decryptFile(String senderId, String sessionId, String filePath, String cipher, String outputPath, Boolean isSticky) {
        String secret = decryptText(senderId, sessionId, cipher, isSticky);
        String path = null;
        if (secret != null)
            path = decryptBlob(filePath, secret, outputPath);
        return path;
    }

    /***
     * 检测会话是否存在
     *
     * @param senderId - 发送方id
     * @param sessionId- 粘性会话id
     * @return boolean
     */
    public Boolean sessionExists(String senderId, String sessionId) {
        SenderKeyStore mySenderKeyStore = new MySenderKeyStore(context);
        SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
        SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
        SenderKeyRecord record = mySenderKeyStore.loadSenderKey(senderKeyName);
        return !record.isEmpty();
    }

    /***
     * 获取粘性会话的当前链步数
     *
     * @param userId 用户ID
     * @param sessionId 粘性会话ID
     * @return 当前链步数（整数值）
     *         如果发生错误返回9999
     */
    public int getChainStep(String userId, String sessionId) {
        SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
        SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userId, 0);
        SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
        try {
            int step = senderKeyStore.loadSenderKey(senderKeyName).getSenderKeyState().getSenderChainKey().getIteration();
            return step;
        } catch (InvalidKeyIdException e) {
            e.printStackTrace();
            return 9999;
        }
    }

    /***
     * 对粘性会话的链进行棘轮操作（前进一步或多步）
     * 用于确保所有设备上的会话状态同步
     *
     * @param userId 用户ID
     * @param sessionId 粘性会话ID
     * @param steps 要前进的步数
     * @throws NoSessionException 如果会话不存在
     */
    public void ratchetChain(String userId, String sessionId, int steps) throws NoSessionException {
        SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userId, 0);
        SenderKeyName senderKeyName = new SenderKeyName(sessionId, signalProtocolAddress);
        SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
        GroupCipher groupCipher = new GroupCipher(senderKeyStore, senderKeyName);
        groupCipher.ratchetChain(steps);
    }

    /**
     * 重新初始化用户自己的粘性会话
     * 用于从备份恢复会话状态或设备间同步
     *
     * @param userId 用户ID
     * @param senderKey JSON对象包含：
     *                  * id - 密钥ID（整型）
     *                  * key - 加密的发送方密钥（格式：链密钥||签名私钥||签名公钥）
     *                  * sessionId - 粘性会话ID（字符串）
     *                  * identityKeyId - 用于加密的身份密钥ID（整型）
     *                  * step - 会话的当前步数（整型）
     */
    public void reinitMyStickySession(String userId, JSONObject senderKey) throws IOException, InvalidKeyException, NoSessionException, JSONException {
        SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(userId, 0);
        SenderKeyName senderKeyName = new SenderKeyName(senderKey.getString("sessionId"), signalProtocolAddress);
        SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
        SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName);

        String key = decryptStickyKey(userId, senderKey.getString("key"), senderKey.getInt("identityKeyId"));
        if (key == null) return;
        String chainKey = key.substring(0, 44);
        String signaturePrivateKey = key.substring(44, 88);
        String signaturePublicKey = key.substring(88, 132);

        ECPublicKey senderPubKey = Curve.decodePoint(Base64.decode(signaturePublicKey), 0);
        ECPrivateKey senderPrivKey = Curve.decodePrivatePoint(Base64.decode(signaturePrivateKey));
        ECKeyPair signatureKey = new ECKeyPair(senderPubKey, senderPrivKey);

        senderKeyRecord.setSenderKeyState(
                senderKey.getInt("id"),
                0,
                Base64.decode(chainKey),
                signatureKey
        );
        senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord);

        // Store initial sender key
        GroupSessionBuilder groupSessionBuilder = new GroupSessionBuilder(senderKeyStore);
        SenderKeyDistributionMessage senderKeyDistributionMessage = groupSessionBuilder.create(senderKeyName);
        DatabaseFactory.getStickyKeyDatabase(context).insertStickyKey(senderKey.getString("sessionId"), Base64.encodeBytes(senderKeyDistributionMessage.serialize()));

        // RATCHET CHAIN
        GroupCipher groupCipher = new GroupCipher(senderKeyStore, senderKeyName);
        groupCipher.ratchetChain(senderKey.getInt("step"));
    }

    /***
     * 解密粘性密钥（发送方密钥）
     * 在解密前根据需要切换身份密钥
     *
     * @param senderId 发送方用户ID
     * @param cipher 加密的密钥数据
     * @param identityKeyId 加密时使用的身份密钥ID
     * @return 解密后的密钥字符串，失败返回null
     */
    public String decryptStickyKey(String senderId, String cipher, int identityKeyId) {
        int activeIdentityKeyId = Preferences.getActiveIdentityKeyId(context);
        // Swap identity key if needed
        boolean success = true;
        if (activeIdentityKeyId != identityKeyId)
            success = swapIdentityKey(identityKeyId);
        if (!success) return null;
        String key = decryptTextPairwise(senderId, true, cipher);
        // Reverse identity key back if was swapped
        if (activeIdentityKeyId != identityKeyId)
            swapIdentityKey(activeIdentityKeyId);
        return key;
    }

    /***
     * 切换当前活动的身份密钥
     *
     * @param keyId 要激活的身份密钥ID
     * @return 切换是否成功（布尔值）
     */
    public boolean swapIdentityKey(int keyId) {
        IdentityKeyDatabase identityKeyDatabase = DatabaseFactory.getIdentityKeyDatabase(context);
        IdentityKeyRecord identityKeyRecord = identityKeyDatabase.getIdentityKey(keyId);
        if (identityKeyRecord == null) {
            return false;
        }
        String publicKey = Base64.encodeBytes(identityKeyRecord.getKeyPair().getPublicKey().serialize());
        String privateKey = Base64.encodeBytes(identityKeyRecord.getKeyPair().getPrivateKey().serialize());
        IdentityKeyUtil.setActive(context, "pref_identity_public", publicKey);
        IdentityKeyUtil.setActive(context, "pref_identity_private", privateKey);
        return true;
    }


    /****************************** 用户密钥管理方法 ******************************/

    /**
     * 刷新身份密钥（定期轮换）
     * 当当前密钥超过有效期时生成新密钥并激活
     *
     * @param identityKeyAge 身份密钥有效期（毫秒）
     * @return 包含新身份密钥的JSON对象，结构如下：
     *         * id - 密钥ID（整型）
     *         * public - 公钥（Base64字符串）
     *         * cipher - 加密的私钥（Base64字符串）
     *         * salt - 加密盐值（Base64字符串）
     *         * timestamp - 时间戳（长整型）
     *         如果未过期则返回null
     */
    public JSONObject refreshIdentityKey(long identityKeyAge) throws Exception {
        // 计算当前密钥已使用时长
        long activeDuration = System.currentTimeMillis() - Preferences.getActiveIdentityKeyTimestamp(context);

        // 检查是否超过有效期
        if (activeDuration > identityKeyAge) {
            // 生成新身份密钥对
            SignalProtocolStore store = new MyProtocolStore(context);
            IdentityKeyUtil.generateIdentityKeys(context);
            IdentityKeyPair identityKey = store.getIdentityKeyPair();

            // 获取用户密码
            String userId = PreferenceManager.getDefaultSharedPreferences(context).getString("userId", "");
            HashMap<String, String> serviceMap = new HashMap();
            serviceMap.put("service", passwordKey);
            String password = keychain.getGenericPassword(userId, serviceMap);

            // 构建密钥JSON对象
            JSONObject identityKeyJson = new JSONObject();
            identityKeyJson.put("id", Preferences.getActiveIdentityKeyId(context));
            identityKeyJson.put("public", Base64.encodeBytes(identityKey.getPublicKey().serialize()));

            // 加密私钥
            HashMap<String, String> signedCipherMap = pbEncrypt(identityKey.getPrivateKey().serialize(), password);
            identityKeyJson.put("cipher", signedCipherMap.get("cipher"));
            identityKeyJson.put("salt", signedCipherMap.get("salt"));
            identityKeyJson.put("timestamp", Long.toString(Preferences.getActiveIdentityKeyTimestamp(context)));

            return identityKeyJson;
        }
        return null;
    }

    /**
     * 刷新已签名的预密钥（定期轮换）
     * 当当前密钥超过有效期时生成新密钥并激活
     *
     * @param signedPreKeyAge 签名预密钥有效期（毫秒）
     * @return 包含新签名预密钥的JSON对象，结构如下：
     *         * id - 密钥ID（整型）
     *         * public - 公钥（Base64字符串）
     *         * cipher - 加密的私钥（Base64字符串）
     *         * salt - 加密盐值（Base64字符串）
     *         * signature - 签名（Base64字符串）
     *         * timestamp - 时间戳（长整型）
     *         如果未过期则返回null
     */
    public JSONObject refreshSignedPreKey(long signedPreKeyAge) throws Exception {
        // 计算当前密钥已使用时长
        long activeDuration = System.currentTimeMillis() - Preferences.getActiveSignedPreKeyTimestamp(context);

        // 检查是否超过有效期
        if (activeDuration > signedPreKeyAge) {
            // 获取当前身份密钥
            IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);

            // 生成新签名预密钥
            SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(context, identityKey, true);

            // 获取用户密码
            String userId = PreferenceManager.getDefaultSharedPreferences(context).getString("userId", "");
            HashMap<String, String> serviceMap = new HashMap();
            serviceMap.put("service", passwordKey);
            String password = keychain.getGenericPassword(userId, serviceMap);

            // 构建密钥JSON对象
            JSONObject signedPreKeyJson = new JSONObject();
            signedPreKeyJson.put("id", Preferences.getActiveSignedPreKeyId(context));
            signedPreKeyJson.put("publicKey", Base64.encodeBytes(signedPreKey.getKeyPair().getPublicKey().serialize()));
            signedPreKeyJson.put("signature", Base64.encodeBytes(signedPreKey.getSignature()));

            // 加密私钥
            HashMap<String, String> signedCipherMap = pbEncrypt(signedPreKey.getKeyPair().getPrivateKey().serialize(), password);
            signedPreKeyJson.put("cipher", signedCipherMap.get("cipher"));
            signedPreKeyJson.put("salt", signedCipherMap.get("salt"));
            signedPreKeyJson.put("timestamp", Long.toString(signedPreKey.getTimestamp()));

            return signedPreKeyJson;
        }
        return null;
    }

    /***
     * 生成预密钥组（用于补充服务器预密钥池）
     * 当可用预密钥数量低于阈值时调用
     *
     * @param nextPreKeyId 下一个预密钥的起始ID
     * @param count 要生成的预密钥数量
     * @return 预密钥数组（JSONArray），每个元素包含：
     *         * id - 密钥ID（整型）
     *         * public - 公钥（Base64字符串）
     *         * cipher - 加密的私钥（Base64字符串）
     *         * salt - 加密盐值（Base64字符串）
     */
    public JSONArray generatePreKeys(int nextPreKeyId, int count) {
        try {
            // 获取用户密码
            String userId = PreferenceManager.getDefaultSharedPreferences(context).getString("userId", "");
            HashMap<String, String> serviceMap = new HashMap();
            serviceMap.put("service", passwordKey);
            String password = keychain.getGenericPassword(userId, serviceMap);

            // 生成预密钥记录
            List<PreKeyRecord> preKeys = PreKeyUtil.generatePreKeys(context, nextPreKeyId, count);
            JSONArray preKeysArray = new JSONArray();

            // 遍历所有预密钥
            for (int i = 0; i < preKeys.size(); i++) {
                JSONObject preKey = new JSONObject();
                preKey.put("id", preKeys.get(i).getId());
                preKey.put("publicKey", Base64.encodeBytes(preKeys.get(i).getKeyPair().getPublicKey().serialize()));

                // 加密私钥
                HashMap<String, String> cipherMap = pbEncrypt(preKeys.get(i).getKeyPair().getPrivateKey().serialize(), password);
                preKey.put("cipher", cipherMap.get("cipher"));
                preKey.put("salt", cipherMap.get("salt"));

                preKeysArray.put(preKey);
            }
            return preKeysArray;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     * 重新加密所有密钥（用于密码变更场景）
     * 使用新密码重新加密身份密钥、签名预密钥和预密钥
     *
     * @param password 新密码（明文）
     * @param progressEvent 进度回调接口（可选）
     * @return 包含所有重新加密密钥的JSON对象，结构如下：
     *         * preKeys - 预密钥数组
     *         * signedPreKeys - 签名预密钥数组
     *         * identityKeys - 身份密钥数组
     */
    public JSONObject reEncryptKeys(String password, ProgressEvent progressEvent)  {
        try {
            // 加载所有密钥记录
            MyProtocolStore store = new MyProtocolStore(context);
            List<PreKeyRecord> preKeys = store.loadPreKeys();
            List<SignedPreKeyRecord> signedPreKeys = store.loadSignedPreKeys();
            List<IdentityKeyRecord> identityKeys = store.loadIdentityKeys();

            int total = identityKeys.size() + signedPreKeys.size() + preKeys.size();
            int progress = 0;

            // 重新加密预密钥
            JSONArray preKeysArray = new JSONArray();
            for (int i=0; i<preKeys.size(); i++) {
                JSONObject key = new JSONObject();
                key.put("id", preKeys.get(i).getId());

                // 使用新密码加密私钥
                HashMap<String, String> cipherMap = pbEncrypt(preKeys.get(i).getKeyPair().getPrivateKey().serialize(), password);
                key.put("cipher", cipherMap.get("cipher"));
                key.put("salt", cipherMap.get("salt"));

                preKeysArray.put(key);

                // 进度回调处理
                progress += 1;
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", progress);
                    event.put("total", total);
                    progressEvent.execute(event);
                }
            }

            // 重新加密签名预密钥
            JSONArray signedPreKeysArray = new JSONArray();
            for (int i=0; i<signedPreKeys.size(); i++) {
                JSONObject key = new JSONObject();
                key.put("id", signedPreKeys.get(i).getId());

                // 使用新密码加密私钥
                HashMap<String, String> cipherMap = pbEncrypt(signedPreKeys.get(i).getKeyPair().getPrivateKey().serialize(), password);
                key.put("cipher", cipherMap.get("cipher"));
                key.put("salt", cipherMap.get("salt"));

                signedPreKeysArray.put(key);

                // 进度回调处理
                progress += 1;
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", progress);
                    event.put("total", total);
                    progressEvent.execute(event);
                }
            }

            // 重新加密身份密钥
            JSONArray identityKeysArray = new JSONArray();
            for (int i=0; i<identityKeys.size(); i++) {
                JSONObject key = new JSONObject();
                key.put("id", identityKeys.get(i).getId());

                // 使用新密码加密私钥
                HashMap<String, String> cipherMap = pbEncrypt(identityKeys.get(i).getKeyPair().getPrivateKey().serialize(), password);
                key.put("cipher", cipherMap.get("cipher"));
                key.put("salt", cipherMap.get("salt"));

                identityKeysArray.put(key);

                // 进度回调处理
                progress += 1;
                if (progressEvent != null) {
                    JSONObject event = new JSONObject();
                    event.put("progress", progress);
                    event.put("total", total);
                    progressEvent.execute(event);
                }
            }

            // 构建返回结果
            JSONObject map = new JSONObject();
            map.put("preKeys", preKeysArray);
            map.put("signedPreKeys", signedPreKeysArray);
            map.put("identityKeys", identityKeysArray);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /****************************** 用户密钥管理方法结束 ******************************/


    /****************************** ARGON2 加密方法 ******************************/

    /***
     * 使用Argon2派生的密钥加密私钥数据
     * 结合Argon2密码派生和AES-CBC加密，提供高安全性密钥保护
     *
     * @param text 要加密的明文数据（字节数组，通常是私钥）
     * @param pass 用户明文密码
     * @return 包含以下内容的HashMap：
     *         - salt: 加密使用的盐值（Base64编码）
     *         - cipher: 加密结果（IV+密文，Base64编码）
     */
    public HashMap<String, String> pbEncrypt(byte[] text, String pass) throws Exception {
        // 生成随机盐值（32字节）
        SecureRandom randomSalt = new SecureRandom();
        byte[] salt = new byte[32];
        randomSalt.nextBytes(salt);

        // 生成随机初始化向量IV（16字节）
        int ivSize = 16;
        byte[] iv = new byte[ivSize];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

        // 使用Argon2从密码派生加密密钥
        byte[] hash = new Argon2.Builder(Version.V13)
                .type(Type.Argon2id)          // 使用Argon2id算法变体
                .memoryCostKiB(4 * 1024)      // 内存成本：4MB
                .parallelism(2)               // 并行度：2个线程
                .iterations(3)                // 迭代次数：3次
                .hashLength(32)               // 输出哈希长度：32字节（256位）
                .build()
                .hash(pass.getBytes(), salt)  // 使用密码和盐值派生密钥
                .getHash();
        SecretKeySpec secretKeySpec = new SecretKeySpec(hash, "AES");

        // 使用AES-CBC模式加密数据
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] encrypted = cipher.doFinal(text);

        // 合并IV和加密数据（IV在前，密文在后）
        byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
        System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
        System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);

        // 返回Base64编码的盐值和加密结果
        HashMap<String, String> map = new HashMap<>();
        map.put("salt", Base64.encodeBytes(salt));
        map.put("cipher", Base64.encodeBytes(encryptedIVAndText));
        return map;
    }

    /***
     * 使用Argon2派生的密钥解密私钥数据
     *
     * @param encryptedIvText 加密数据（Base64编码的IV+密文）
     * @param salt 加密时使用的盐值（Base64编码）
     * @param pass 用户明文密码
     * @return 解密后的原始字节数据
     */
    public byte[] pbDecrypt(String encryptedIvText, String salt, String pass) throws Exception {
        int ivSize = 16;
        // 解码Base64数据
        byte[] encryptedIvTextBytes = Base64.decode(encryptedIvText);

        // 从加密数据中提取IV（前16字节）
        byte[] iv = new byte[ivSize];
        System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

        // 提取加密数据部分（IV之后的内容）
        int encryptedSize = encryptedIvTextBytes.length - ivSize;
        byte[] encryptedBytes = new byte[encryptedSize];
        System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);

        // 使用相同的Argon2参数派生密钥
        byte[] hash = new Argon2.Builder(Version.V13)
                .type(Type.Argon2id)
                .memoryCostKiB(4 * 1024)
                .parallelism(2)
                .iterations(3)
                .hashLength(32)
                .build()
                .hash(pass.getBytes(), Base64.decode(salt))  // 使用相同盐值
                .getHash();

        SecretKeySpec secretKeySpec = new SecretKeySpec(hash, "AES");

        // 使用AES-CBC模式解密数据
        Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipherDecrypt.doFinal(encryptedBytes);
    }

    /****************************** ARGON2 加密方法结束 ******************************/

    /****************************** 文件加密方法 ******************************/

    /**
     * 加密二进制文件（BLOB）
     * 使用AES加密文件内容，并生成文件密钥和哈希值
     *
     * @param filePath 要加密的文件路径
     * @return HashMap 包含：
     *         * uri: 加密文件的路径（file://格式）
     *         * secret: 文件密钥和哈希值的组合（Base64(fileKey) + Base64(fileHash)）
     */
    public HashMap<String, String> encryptBlob(String filePath) {
        try {
            File file = new File(filePath);
            InputStream is = new FileInputStream(file);

            // 创建加密文件流
            CipherFileStream cipherFileStream = CipherFile.newStreamBuilder()
                    .withStream(is)
                    .withLength(is.available()).build();

            // 生成随机文件密钥(64字节)和初始化向量(16字节)
            byte[] fileKey = Util.getSecretBytes(64);
            byte[] fileIV = Util.getSecretBytes(16);

            // 创建带填充的输入流
            InputStream dataStream = new PaddingInputStream(cipherFileStream.getInputStream(), cipherFileStream.getLength());

            // 准备输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            String encryptedFilePath = path + "/" + UUID.randomUUID().toString();
            File encryptedFile = new File(encryptedFilePath);

            // 创建带摘要的加密输出流
            DigestingOutputStream outputStream = new CipherOutputStreamFactory(fileKey, fileIV).createFor(byteArrayOutputStream);

            // 加密文件内容
            byte[] buffer = new byte[4096];
            int read;
            while ((read = dataStream.read(buffer, 0, buffer.length)) != -1) {
                outputStream.write(buffer, 0, read);
            }

            // 关闭流
            byteArrayOutputStream.flush();
            byteArrayOutputStream.close();
            outputStream.flush();
            outputStream.close();

            // 写入加密文件
            FileOutputStream fos = new FileOutputStream(encryptedFile);
            fos.write(byteArrayOutputStream.toByteArray());
            fos.flush();
            fos.close();

            // 获取文件哈希并构建密钥字符串
            byte[] digest = outputStream.getTransmittedDigest();
            String secret = Base64.encodeBytes(fileKey) + Base64.encodeBytes(digest);

            // 返回结果
            HashMap<String, String> map = new HashMap<>();
            map.put("uri", "file://" + encryptedFilePath);
            map.put("secret", secret);
            return map;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /***
     * 解密二进制文件（BLOB）
     *
     * @param filePath 加密文件路径
     * @param secret 文件密钥和哈希组合（Base64(fileKey) + Base64(fileHash)）
     * @param outputPath 解密文件输出路径
     * @return 解密文件的绝对路径（file://格式）
     */
    public String decryptBlob(String filePath, String secret, String outputPath) {
        File file = new File(filePath);
        try {
            // 拆分密钥和哈希
            String key = secret.substring(0, 88);    // Base64编码的64字节密钥
            String digest = secret.substring(88);      // 文件哈希值

            // 创建解密输入流
            InputStream inputStream = CipherInputStream.createForFile(
                    file,
                    file.length(),
                    Base64.decode(key),
                    Base64.decode(digest)
            );

            // 准备输出文件
            File outputFile = new File(outputPath);
            byte[] buffer = new byte[8192];
            int read;
            long total = 0;
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            // 读取并解密数据
            while ((read = inputStream.read(buffer, 0, buffer.length)) != -1) {
                byteArrayOutputStream.write(buffer, 0, read);
                total += read;
            }

            // 写入解密文件
            FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
            fileOutputStream.write(byteArrayOutputStream.toByteArray());
            fileOutputStream.flush();
            fileOutputStream.close();
            byteArrayOutputStream.flush();
            byteArrayOutputStream.close();

            return "file://" + outputPath;
        } catch (IOException | InvalidMessageException e) {
            e.printStackTrace();
        }
        return null;
    }

    /****************************** 文件加密方法结束 ******************************/

    /************************** Signal会话方法 ***************************/

    /***
     * 点对点会话中加密文件
     *
     * @param userId 目标用户ID
     * @param filePath 要加密的文件路径
     * @return JSONObject 包含：
     *         * uri: 加密文件路径
     *         * cipher: 加密的文件密钥（fileKey||fileHash）
     */
    public JSONObject encryptFilePairwise(String userId, String filePath) throws JSONException {
        // 加密文件内容
        HashMap<String, String> hashMap = encryptBlob(filePath);
        // 加密文件密钥
        String cipherText = encryptTextPairwise(userId, hashMap.get("secret"));

        // 返回结果
        JSONObject map = new JSONObject();
        map.put("uri", hashMap.get("uri"));
        map.put("cipher", cipherText);
        return map;
    }

    /***
     * 点对点会话中解密文件
     *
     * @param senderId 发送方ID
     * @param filePath 加密文件路径
     * @param cipher 加密的文件密钥
     * @param outputPath 解密文件输出路径
     * @return 解密文件的绝对路径
     */
    public String decryptFilePairwise(String senderId, String filePath, String cipher, String outputPath) {
        // 解密文件密钥
        String secret = decryptTextPairwise(senderId, false, cipher);
        String path = null;
        if (secret != null)
            // 解密文件内容
            path = decryptBlob(filePath, secret, outputPath);
        return path;
    }

    /***
     * 检查点对点会话是否存在
     *
     * @param oneTimeId 一次性会话ID（UUID）
     * @return 会话是否存在（布尔值）
     */
    public boolean pairwiseSessionExists(String oneTimeId) {
        SignalProtocolStore store = new MyProtocolStore(context);
        SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(oneTimeId, 0);
        return store.containsSession(signalProtocolAddress);
    }

    /**
     * 初始化标准群组会话
     * 使用加密的发送方密钥创建群组会话
     *
     * @param senderId 发送方用户ID
     * @param groupId 群组ID
     * @param cipherSenderKey 加密的发送方密钥
     */
    public void initStandardGroupSession(String senderId, String groupId, String cipherSenderKey) {
        try {
            if (cipherSenderKey != null) {
                SenderKeyStore senderKeyStore = new MySenderKeyStore(context);
                SignalProtocolAddress signalProtocolAddress = new SignalProtocolAddress(senderId, 0);
                SenderKeyName senderKeyName = new SenderKeyName(groupId, signalProtocolAddress);
                GroupSessionBuilder groupSessionBuilder = new GroupSessionBuilder(senderKeyStore);

                // 解密发送方密钥
                String senderKey = decryptTextPairwise(senderId, false, cipherSenderKey);
                if (senderKey != null) {
                    // 创建并处理发送方密钥分发消息
                    SenderKeyDistributionMessage senderKeyDistributionMessage =
                            new SenderKeyDistributionMessage(Base64.decode(senderKey));
                    groupSessionBuilder.process(senderKeyName, senderKeyDistributionMessage);
                }
            }
        } catch (InvalidMessageException | LegacyMessageException | IOException e) {
            e.printStackTrace();
        }
    }

    /************************** Signal会话方法结束 ***************************/

    /****************************** 工具方法 ******************************/

    /***
     * 从安全存储中恢复用户密码
     *
     * @param userId 用户ID
     * @return 存储的密码（字符串）
     */
    public String recoverPassword(String userId) {
        HashMap<String, String> serviceMap = new HashMap();
        serviceMap.put("service", passwordKey);
        return keychain.getGenericPassword(userId, serviceMap);
    }

    /**
     * 重置数据库（清除所有密钥数据）
     * 用于用户注销或安全擦除场景
     */
    public void resetDatabase() {
        DatabaseFactory.getInstance(context).resetDatabase(context);
        Preferences.resetPreferences(context);
    }

    /**
     * 检查用户是否已完成初始化
     *
     * @return 初始化状态（布尔值）
     */
    public boolean isInitialized() {
        SignalProtocolStore store = new MyProtocolStore(context);
        int localId = store.getLocalRegistrationId();
        return localId != 0;  // 注册ID不为0表示已初始化
    }

    /***
     * 获取接收方缓存（单例模式）
     * 用于Recipient.java类的辅助方法
     */
    public static synchronized @NonNull
    LiveRecipientCache getRecipientCache() {
        if (recipientCache == null) {
            recipientCache = new LiveRecipientCache(context);
        }
        return recipientCache;
    }

    /****************************** 工具方法结束 ******************************/

}
